package com.whu.zqm.myioc;

import com.whu.zqm.myioc.beanshuxing.BeanDefinition;
import com.whu.zqm.myioc.beanshuxing.MutablePropertyValues;
import com.whu.zqm.myioc.beanshuxing.PropertyValue;
import com.whu.zqm.myioc.xmlreader.BeanDefinitionReader;
import com.whu.zqm.myioc.xmlreader.BeanDefinitionRegistry;
import com.whu.zqm.myioc.xmlreader.XmlBeanDefinitionReader;

import java.lang.reflect.Method;

import java.util.HashMap;
import java.util.Map;

public class MiniApplicationContext {
    protected final Map<String, Object> singletonObjectMap = new HashMap<>();
    protected BeanDefinitionReader beanDefinitionReader;
    protected String configLocation;

    public MiniApplicationContext(String configLocation) throws Exception {
        this.configLocation = configLocation;
        beanDefinitionReader = new XmlBeanDefinitionReader();
        this.refresh();
    }

    public void refresh() throws Exception {
        // 加载 beanDefinition 对象
        beanDefinitionReader.loadBeanDefinitions(configLocation);
        // 执行 bean 的初始化
        finishBeanInitialization();
    }

    private void finishBeanInitialization() throws Exception {
        // 获取 BeanDefinitionRegistry 对象
        BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
        // 获取所有的 beanDefinitionName
        String[] beanDefinitionNames = registry.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            // 这里的 getBean 方法用到了模板方法模式,是在子实现类中完成的
            getBean(beanDefinitionName);
        }
    }



    public Object getBean(String name) throws Exception {
        // 先判断容器中是否存在该 bean 对象,存在就直接返回
        Object obj = singletonObjectMap.get(name);
        if (null != obj) {
            return obj;
        }
        BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
        // 从 BeanDefinitionRegistry 中根据名称取出 BeanDefinition
        BeanDefinition beanDefinition = registry.getBeanDefinition(name);
        if (null == beanDefinition) {
            return null;
        }

        // 通过反射创建bean
        Class<?> clazz = Class.forName(beanDefinition.getClassName());
        Object beanObj = clazz.newInstance();

        // 获取依赖信息
        MutablePropertyValues mutablePropertyValues = beanDefinition.getMutablePropertyValues();
        for (PropertyValue mutablePropertyValue : mutablePropertyValues) {
            String propertyValueName = mutablePropertyValue.getName();
            String ref = mutablePropertyValue.getRef();

            if (null != ref && !"".equals(ref.trim())) {
                // 获取需要注入的 bean,这里类似于递归
                Object bean = getBean(ref);
                // 组装 beanObj 中 setBean 的方法名称
                String setMethodName = buildSetMethodName(propertyValueName);
                // 获取 beanObj 中 setBean 的方法,并进行设值
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (method.getName().equals(setMethodName)) {
                        method.invoke(beanObj, bean);
                    }
                }
            }
        }
        // 将创建出来的对象放入 ioc 容器中
        singletonObjectMap.put(name, beanObj);
        return beanObj;
    }


    public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {
        Object bean = getBean(name);
        if (null == bean) {
            return null;
        }
        return clazz.cast(bean);
    }

    private String buildSetMethodName(String name) {
        if (null == name || "".equals(name.trim())) {
            return "";
        }
        String upperString = name.substring(0, 1).toUpperCase() + name.substring(1);
        return "set" + upperString;
    }
}